﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;

namespace Leo.Common.Json
{
    public class JsonObject
    {

        private readonly StringComparison sComparison;

        private readonly JToken JO;
        private JsonObject(JToken jo, StringComparison Comparison)
        {
            this.sComparison = Comparison;
            this.JO = jo;
        }

        private JsonObject(JToken jo, bool ignoreCase) : this(jo, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)
        {
        }

        public static JsonObject Parse(string json, bool ignoreCases = false)
        {
            JToken jtoken = JToken.Parse(json);
            if (jtoken == null)
            {
                return null;
            }
            return new JsonObject(jtoken, ignoreCases);
        }

        public static JsonObject New(bool ignoreCases = false)
        {
            return new JsonObject(new JObject(), ignoreCases);
        }

        public static JsonObject From<T>(T obj, bool ignoreCase = false) where T : class
        {
            if (obj == null)
            {
                return JsonObject.New(ignoreCase);
            }
            JsonObject jsonObject = obj as JsonObject;
            if (jsonObject != null)
            {
                return jsonObject;
            }
            JToken jtoken = obj as JToken;
            if (jtoken != null)
            {
                return new JsonObject(jtoken, ignoreCase);
            }
            if (obj.GetType() == typeof(string))
            {
                throw new ArgumentException("不支持从字符串初始化Json对象");
            }
            JToken jtoken2 = JToken.FromObject(obj);
            if (jtoken2 == null)
            {
                return null;
            }
            return new JsonObject(jtoken2, ignoreCase);
        }

        private JObject GetJObject()
        {
            JObject jobject = this.JO as JObject;
            if (jobject != null)
            {
                return jobject;
            }
            return null;
        }

        public string GetString(string propertyName)
        {
            if (propertyName == null)
            {
                return null;
            }
            if (!propertyName.Contains("/"))
            {
                JObject jobject = this.GetJObject();
                JToken token;
                if (jobject == null)
                {
                    token = null;
                }
                else
                {
                    JProperty jproperty = jobject.Property(propertyName, this.sComparison);
                    token = ((jproperty != null) ? jproperty.Value : null);
                }
                return JsonObject.GetString(token);
            }
            string text = propertyName.Substring(0, propertyName.IndexOf('/'));
            if (!text.Contains("["))
            {
                JsonObject @object = this.GetObject(text);
                if (@object == null)
                {
                    return null;
                }
                return @object.GetString(propertyName.Substring(text.Length + 1));
            }
            else
            {
                Match match = Regex.Match(text, "^([^\\[]+)\\[(\\d+)\\]$");
                string value = match.Groups[1].Value;
                int num = int.Parse(match.Groups[2].Value);
                JsonObject[] array = this.GetArray(value);
                if (array == null || array.Length <= num)
                {
                    return null;
                }
                return array[num].GetString(propertyName.Substring(text.Length + 1));
            }
        }

        private static string GetString(JToken token)
        {
            if (token == null)
            {
                return null;
            }
            if (token.Type == JTokenType.Object || token.Type == JTokenType.Array || token.Type == JTokenType.Property || token.Type == JTokenType.Boolean)
            {
                return token.ToString(0, Array.Empty<JsonConverter>());
            }
            return token.ToString();
        }
 
        public int GetInt(string propertyName)
        {
            return Convert.ToInt32(this.GetString(propertyName));
        }

        public T Get<T>(string propertyName)
        {
            JObject jobject = this.JO as JObject;
            if (jobject == null)
            {
                return default(T);
            }
            JProperty jproperty = jobject.Property(propertyName);
            if (jproperty == null || jproperty.Value == null)
            {
                return default(T);
            }
            return Extensions.Value<T>(jproperty.Value);
        }

        public bool TryGetValue<T>(string propertyName, out T value)
        {
            bool result;
            try
            {
                value = this.Get<T>(propertyName);
                result = (value != null);
            }
            catch
            {
                value = default(T);
                result = false;
            }
            return result;
        }

        public Dictionary<string, JsonObject> GetProperties()
        {
            JObject jobject = this.JO as JObject;
            if (jobject == null)
            {
                return null;
            }
            IEnumerable<JProperty> enumerable = jobject.Properties();
            if (enumerable == null || Enumerable.Count<JProperty>(enumerable) == 0)
            {
                return null;
            }
            Dictionary<string, JsonObject> dictionary = new Dictionary<string, JsonObject>();
            foreach (JProperty jproperty in enumerable)
            {
                dictionary.Add(jproperty.Name, new JsonObject(jproperty.Value, this.sComparison));
            }
            return dictionary;
        }

        public long GetLong(string propertyName)
        {
            return Convert.ToInt64(this.GetString(propertyName));
        }


        public decimal GetDecimal(string propertyName)
        {
            return Convert.ToDecimal(this.GetString(propertyName));
        }

        public bool GetBoolean(string propertyName)
        {
            return Convert.ToBoolean(this.GetString(propertyName));
        }


        public DateTime GetDateTime(string propertyName)
        {
            return Convert.ToDateTime(this.GetString(propertyName));
        }


        public JsonObject GetObject(string propertyName)
        {
            JObject jobject = this.GetJObject();
            JProperty jproperty = (jobject != null) ? jobject.Property(propertyName, this.sComparison) : null;
            if (((jproperty != null) ? jproperty.Value : null) == null)
            {
                return null;
            }
            return new JsonObject(jproperty.Value, this.sComparison);
        }


        public JsonObject[] GetArray(string propertyName = null)
        {
            if (propertyName == null)
            {
                return this.ToArray();
            }
            JsonObject @object = this.GetObject(propertyName);
            if (@object == null)
            {
                return null;
            }
            return @object.GetArray(null);
        }


        private JsonObject[] ToArray()
        {
            JArray jarray = this.JO as JArray;
            if (jarray != null)
            {
                return Enumerable.ToArray<JsonObject>(Enumerable.Select<JToken, JsonObject>(jarray, (JToken x) => new JsonObject(x, this.sComparison)));
            }
            return null;
        }


        public JsonObject Put(string propertyName, object value)
        {
            JsonObject jsonObject = value as JsonObject;
            if (jsonObject != null)
            {
                JObject jobject = this.GetJObject();
                if (jobject != null)
                {
                    jobject.Add(propertyName, jsonObject.JO);
                }
                return this;
            }
            JObject jobject2 = this.GetJObject();
            if (jobject2 != null)
            {
                jobject2.Add(propertyName, JToken.FromObject(value));
            }
            return this;
        }


        public override string ToString()
        {
            return JsonObject.GetString(this.JO);
        }


        public string ToString(Formatting formatting)
        {
            return this.JO.ToString(formatting, Array.Empty<JsonConverter>());
        }


    }
}
